/*
 * Copyright 2024 The CHANGLEI Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import 'dart:convert';
import 'dart:io';

import 'package:fibber/src/logger.dart';
import 'package:fibber/src/name.dart';
import 'package:fibber/src/spec.dart';
import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';
import 'package:yaml_edit/yaml_edit.dart';

const _flutterKey = r'flutter';
const _fontsKey = r'fonts';
const _familyKey = r'family';
const _assetKey = r'asset';
const _styleKey = r'style';
const _weightKey = r'weight';

Future<void> writePubspecYaml(File pubspecFile, String ttfOutput, Name classname) async {
  final ttfOutputFile = File(path.join(ttfOutput, '${classname.headerCased}.ttf'));
  final content = pubspecFile.readAsStringSync();

  final yamlMap = loadYaml(content) as Map?;
  final flutterNode = yamlMap?[_flutterKey] as Map?;
  if (yamlMap == null || flutterNode == null) {
    Level.error.writeln('不是一个`flutter`项目，或者添加`flutter`节点。');
    return;
  }

  final asset = ttfOutputFile.path;
  final family = classname.titleCased;
  final yamlContents = <String>[
    r'flutter:',
    '${indent}fonts:',
    '${indent * 2}- family: $family',
    '${indent * 3}fonts:',
    '${indent * 4}- asset: $asset',
  ];

  final fontsNode = flutterNode[_fontsKey] as List?;
  final fontsYamlList = List.of(_resolveFonts(fontsNode, asset, family));
  try {
    final yamlEditor = YamlEditor(content);
    yamlEditor.update([_flutterKey, _fontsKey], fontsYamlList);
    final newContent = yamlEditor.toString();
    if (newContent != content) {
      final writer = pubspecFile.openWrite();
      await writer.flush();
      writer.write(newContent);
      await writer.close();

      Level.verbose.writeln(r'Running -> flutter pub get');
      final process = await Process.start(r'flutter', [r'pub', r'get'], runInShell: true);
      final subscription = process.stdout.transform(utf8.decoder).listen(Level.verbose.writeln);
      await process.exitCode;
      await subscription.cancel();
    }

    Level.warn.writeAll(
      <String>[
        r'已在`pubspec.yaml`中添加以下内容：',
        ...yamlContents,
      ],
      '\n',
    );
  } catch (e) {
    Level.warn.writeAll(
      <String>[
        r'请在`pubspec.yaml`中添加以下内容：',
        ...yamlContents,
      ],
      '\n',
    );
    rethrow;
  } finally {
    Level.normal.writeln();
  }
}

Iterable<dynamic> _resolveFonts(List<dynamic>? fontsNode, String newAsset, String newFamily) sync* {
  final newFont = <String, dynamic>{
    _familyKey: newFamily,
    _fontsKey: <Map<String, dynamic>>[
      <String, dynamic>{
        _assetKey: newAsset,
      },
    ],
  };
  if (fontsNode == null) {
    yield newFont;
    return;
  }
  bool isContainsFamily = false;
  for (final node1 in fontsNode) {
    final family = node1[_familyKey] as String;
    final fonts2 = node1[_fontsKey] as YamlList;
    final fonts2List = <dynamic>[];
    bool isContainsAsset = false;
    for (final node2 in fonts2) {
      if (node2 is! Map) {
        continue;
      }
      final asset = node2[_assetKey] as String;
      fonts2List.add(<String, dynamic>{
        _assetKey: asset,
        if (node2.containsKey(_styleKey)) _styleKey: node2[_styleKey],
        if (node2.containsKey(_weightKey)) _weightKey: node2[_weightKey],
      });
      if (!isContainsAsset && asset == newAsset) {
        isContainsAsset = true;
      }
    }
    if (!isContainsFamily && family == newFamily) {
      isContainsFamily = true;
      if (!isContainsAsset) {
        fonts2List.add(<String, dynamic>{
          _assetKey: newAsset,
        });
      }
    }
    yield <String, dynamic>{
      _familyKey: family,
      _fontsKey: fonts2List,
    };
  }
  if (!isContainsFamily) {
    yield newFont;
  }
}
